package br.ufc.apsoo.repositories;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.joda.time.LocalDate;

import br.com.caelum.vraptor.ioc.Component;
import br.ufc.apsoo.models.Apartamento;
import br.ufc.apsoo.models.Reserva;
import br.ufc.apsoo.models.Situacao;

@Component
public class ApartamentoRepositoryImpl extends
		Repository<Apartamento, Serializable> implements TodosApartamentos {

	private Session session;

	protected ApartamentoRepositoryImpl(EntityManager entityManager) {
		super(entityManager);
		session = (Session) entityManager.getDelegate();
	}

	@Override
	public List<Apartamento> naoReservados(Reserva reserva) {
		Boolean pesquisaPorTipo = (reserva.getApartamento().getTipoApartamento() != null
				&& reserva.getApartamento().getTipoApartamento().getId() != null
				&& reserva.getApartamento().getTipoApartamento().getId() != 0);
		
		StringBuilder hqlApartamentos = new StringBuilder();
		StringBuilder hqlReservas = new StringBuilder();
		
		hqlReservas.append("select	r.apartamento.numero ");
		hqlReservas.append("from	br.ufc.apsoo.models.Reserva r ");
		hqlReservas.append("where	(r.situacao = :reservado or r.situacao = :ocupado) ");
		hqlReservas.append("and		((r.dataEntrada between :dataEntrada and :dataSaida) ");
		hqlReservas.append("		 or (r.dataSaida between :dataEntrada and :dataSaida) ");
		hqlReservas.append("		 or (:dataEntrada between r.dataEntrada and r.dataSaida) ");
		hqlReservas.append("		 or (:dataSaida between r.dataEntrada and r.dataSaida)) ");
		if (pesquisaPorTipo) {
			hqlReservas.append("and	r.apartamento.tipoApartamento.id = :tipoApartamentoId ");
		}
		
		hqlApartamentos.append("select	a ");
		hqlApartamentos.append("from	br.ufc.apsoo.models.Apartamento a ");
		hqlApartamentos.append("where	a.numero not in ( ");
		hqlApartamentos.append(hqlReservas.toString());
		hqlApartamentos.append(") ");

		if (pesquisaPorTipo) {
			hqlApartamentos
					.append("and	a.tipoApartamento.id = :tipoApartamentoId ");
		}

		Query query = entityManager.createQuery(hqlApartamentos.toString())
				.setParameter("reservado", Situacao.RESERVADO)
				.setParameter("ocupado", Situacao.OCUPADO)
				.setParameter("dataEntrada", reserva.getDataEntrada())
				.setParameter("dataSaida", reserva.getDataSaida());

		if (pesquisaPorTipo) {
			query.setParameter("tipoApartamentoId", reserva.getApartamento()
					.getTipoApartamento().getId());
		}

		return query.getResultList();
	}

	@Override
	public List<Apartamento> ocupados() {
		StringBuilder hqlBuilder = new StringBuilder();

		hqlBuilder.append("select r.apartamento ");
		hqlBuilder.append("from   br.ufc.apsoo.models.Reserva r ");
		hqlBuilder.append("where  r.situacao = :situacao ");
		hqlBuilder.append("order by r.apartamento.numero");

		return entityManager.createQuery(hqlBuilder.toString())
				.setParameter("situacao", Situacao.OCUPADO).getResultList();
	}

	@Override
	public Apartamento trazPorNumero(Integer numero) {
		Criteria criteria = session.createCriteria(Apartamento.class);
		return (Apartamento) criteria.add(Restrictions.eq("numero", numero))
				.uniqueResult();
	}

	@Override
	public List<Apartamento> ocupadosEm(Date data) {
		
		Criteria criteria = session.createCriteria(Reserva.class);
		criteria.setProjection(Projections.property("apartamento"));
		criteria.createAlias("apartamento", "apartamento");
		criteria.add(Restrictions.le("dataEntrada", data));
		criteria.add(Restrictions.ge("dataSaida", data));
		
		LocalDate dataEscolhida = new LocalDate(data);
		
		int comparator = dataEscolhida.compareTo(new LocalDate(new Date())); 
		
		if (comparator < 0) {
			criteria.add(Restrictions.in("situacao", new Object[] {Situacao.LIVRE, Situacao.OCUPADO}));
		} else {
			if (comparator > 0) {
				criteria.add(Restrictions.in("situacao", new Object[] {Situacao.OCUPADO, Situacao.RESERVADO}));
			} else {
				criteria.add(Restrictions.in("situacao", new Object[] {Situacao.OCUPADO}));
			}
		}
		return criteria.list();
	}

	@Override
	public List<Apartamento> traz() {
		return findAll();
	}
	
	
}
